﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using CommonLib;

namespace ModbusProject
{
    public partial class Form1 : Form
    {
        private SerialPort serialPort;
        private const byte DeviceAddress = 0x01;//设备地址
        private const ushort TempRegisterAddres = 0x0400;//温度寄存器地址
        private const ushort RegisterCount = 0x0001;//读取1个寄存器（1路传感器）

        private bool isReading = false;
        private Thread autoReadThread;
        public Form1()
        {
            InitializeComponent();
            InitUi();
        }

        public void InitUi()
        {
            // 加载可用COM口
            cboComPort.Items.AddRange(SerialPort.GetPortNames());
            if (cboComPort.Items.Count > 0)
                cboComPort.SelectedIndex = 0;
        }

        /// <summary>
        /// 初始化串口
        /// </summary>
        /// <param name="portName"></param>
        public void InitSerialPort(string portName)
        {
            serialPort = new SerialPort
            {
                PortName = portName,
                BaudRate = 9600,
                DataBits = 8,
                StopBits = StopBits.One,
                Parity = Parity.None,
                ReadTimeout = 500,
                WriteTimeout = 500
            };
        }

        public bool OpenPort()
        {
            if (serialPort == null || serialPort.IsOpen) return false;
            try
            {
                serialPort.Open();
                btnOpenClose.Text = "关闭串口";
                btnRead.Enabled = true;
                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"串口打开失败：{ex.Message}");
                return false;
            }

        }

        public bool ClosePort()
        {
            //serialPort不为空，调用 isopen，否则直接返回null
            if (serialPort?.IsOpen ?? false)
            {
                serialPort.Close();
                btnOpenClose.Text = "打开串口";
                btnRead.Enabled = false;
            }

            return true;
        }

        // 构建读寄存器请求帧
        private byte[] BuildReadRequest()
        {
            //设备地址(1) + 功能码(1) + 寄存器地址(2) + 寄存器数量(2) + CRC(2)
            byte[] bytes = new byte[6];
            bytes[0] = DeviceAddress;
            bytes[1] = 0x04;//功能码
            bytes[2] = TempRegisterAddres >> 8;//寄存器地址高位
            bytes[3] = TempRegisterAddres & 0xFF;//寄存器地址低位
            bytes[4] = RegisterCount >> 8;//寄存器数量高位
            bytes[5] = RegisterCount & 0xFF;//寄存器数量低位

            ushort crc = CalculateCrc(bytes.Take(6).ToArray());//计算crc
            return bytes.Concat(new byte[] { (byte)(crc & 0xFF), (byte)(crc >> 8) }).ToArray();

        }

        /// <summary>
        /// 解析温度值
        /// </summary>
        /// <param name="response"></param>
        /// <returns></returns>
        private double ParseTemperature(byte[] response)
        {
            // 响应帧结构：设备地址(1) + 功能码(1) + 数据长度(1) + 温度数据(2) + CRC(2)
            if (response[1] != 0x04 || response[2] != 0x02)
                throw new Exception("响应格式错误！");

            //提取温度数据
            int highByte = response[3];
            int lowByte = response[4];
            int value = (highByte << 8) | lowByte;
            //处理负数
            if ((value & 0x8000) != 0)//最高位为1表示负数
            {
                value = -(0x10000 - value);
            }
            return value / 10.0;//转换为温度值
        }

        public double? ReadTemperature()
        {
            if (serialPort == null || !serialPort.IsOpen)
            {
                MessageBox.Show("串口未打开");
                return null;
            }
            try
            {
                //1.构建modbus请求
                byte[] request = BuildReadRequest();
                serialPort.Write(request, 0, request.Length);
                //2.读取响应
                Thread.Sleep(100);
                int responseLength = serialPort.BytesToRead;
                if (responseLength < 7)
                {
                    MessageBox.Show("响应数据长度异常！");
                    return null;
                }
                byte[] response = new byte[responseLength];
                serialPort.Read(response, 0, response.Length);
                //3.校验crc
                if (!CheckCrc(response))
                {
                    MessageBox.Show("CRC校验失败!");
                    return null;
                }
                //4.解析温度数据
                return ParseTemperature(response);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"读取失败：{ex.Message}");
                return null;
            }
        }

        /// <summary>
        ///  CRC16计算
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private ushort CalculateCrc(byte[] data)
        {
            ushort crc = 0xFFFF;
            foreach (byte b in data)
            {
                crc ^= (ushort)b;
                for (int i = 0; i < 8; i++)
                {
                    if ((crc & 0x0001) != 0)
                    {
                        crc >>= 1;
                        crc ^= 0xA001;
                    }
                    else
                    {
                        crc >>= 1;
                    }
                }
            }
            return crc;
        }

        /// <summary>
        /// 验证响应的CRC
        /// </summary>
        /// <param name="response"></param>
        /// <returns></returns>
        private bool CheckCrc(byte[] response)
        {
            if (response.Length < 2) return false;
            // 提取响应中的CRC
            ushort receivedCrc = (ushort)((response[response.Length - 1] << 8) | response[response.Length - 2]);
            // 计算数据部分的CRC
            ushort calculatedCrc = CalculateCrc(response.Take(response.Length - 2).ToArray());
            return receivedCrc == calculatedCrc;
        }

        private void btnOpenClose_Click(object sender, EventArgs e)
        {
            if (serialPort == null || !serialPort.IsOpen)
            {
                InitSerialPort(cboComPort.SelectedItem.ToString());
                OpenPort();
            }
            else
            {
                ClosePort();
            }
        }

        private void btnRead_Click(object sender, EventArgs e)
        {
            double? temperature = ReadTemperature();
            if (temperature.HasValue)
            {
                lblTemperature.Text = temperature.Value.ToString("F1") + " °C";
            }
        }

        private void btnAutoRead_Click(object sender, EventArgs e)
        {
            isReading = !isReading;
            if(isReading)
            {
                btnAutoRead.Text = "停止读取";
                autoReadThread = new Thread(AutoReadTemperature);
                autoReadThread.Start();
            }
            else
            {
                btnAutoRead.Text = "读取温度";
                if (autoReadThread != null && autoReadThread.IsAlive)
                {
                    autoReadThread.Join();
                }
            }
        }

        private void AutoReadTemperature()
        {
            while(isReading)
            {
                double? temp = ReadTemperature();
                if (temp.HasValue)
                {
                    this.Invoke((MethodInvoker)delegate
                    {
                        lblTemperature.Text = temp.Value.ToString("F1") + " °C";
                    });
                }
                Thread.Sleep(1000);
            }
            
        }


    }
}
